package fun.ticsmyc.rpc.nacos.registry.impl;

import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import fun.ticsmyc.rpc.common.entity.TRPCServiceProperties;
import fun.ticsmyc.rpc.common.enumeration.RpcError;
import fun.ticsmyc.rpc.common.exception.RpcException;
import fun.ticsmyc.rpc.nacos.registry.ServiceRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 * 注册中心 【无状态 做成单例懒加载模式】
 * @author Ticsmyc
 * @date 2020-10-29 11:12
 */

public class NacosServiceRegistryImpl implements ServiceRegistry {
    private static final Logger logger = LoggerFactory.getLogger(NacosServiceRegistryImpl.class);

    private static NamingService namingService = NacosNameServiceProvider.getInstance();

    private static ConcurrentHashMap<TRPCServiceProperties,InetSocketAddress> registedServices;

    //标识是否完成所有服务的注销
    private volatile static boolean deregistered=false;

    private volatile static NacosServiceRegistryImpl INSTANCE;
    private NacosServiceRegistryImpl(){}
    public static NacosServiceRegistryImpl getInstance(){
        if(INSTANCE == null){
            synchronized (NacosServiceRegistryImpl.class){
                if(INSTANCE == null){
                    INSTANCE = new NacosServiceRegistryImpl();
                    registedServices = new ConcurrentHashMap<>();
                    //注册一个回调， 在jvm退出时，注销所有服务
                    Runtime.getRuntime().addShutdownHook(new Thread(()-> NacosServiceRegistryImpl.getInstance().deregisterAll()));
                }
            }
        }
        return INSTANCE;
    }

    @Override
    public void register(TRPCServiceProperties trpcServiceProperties, InetSocketAddress inetSocketAddress) {
        try {

            namingService.registerInstance(trpcServiceProperties.getSignature(),inetSocketAddress.getHostName(),inetSocketAddress.getPort());
            registedServices.put(trpcServiceProperties,inetSocketAddress);
        } catch (NacosException e) {
            logger.error("注册服务时有错误发生:",e);
            throw new RpcException(RpcError.REGISTER_SERVICE_FAILED);
        }
    }

    /**
     * 从nacos注销服务。
     * 可能会有多个线程同时处理注销事件， deregister方法线程不安全，
     * 可能有多个线程 来注销同一个trpcServiceProperties
     * 所以加锁 ， 锁句柄使用trpcServiceProperties 对象本身
     * @param trpcServiceProperties
     * @param inetSocketAddress
     */
    @Override
    public void deregister(TRPCServiceProperties trpcServiceProperties, InetSocketAddress inetSocketAddress) {
        try {
            if(registedServices.containsKey(trpcServiceProperties)){
                logger.debug("从nacos注销服务{}",trpcServiceProperties);
                registedServices.remove(trpcServiceProperties);
                namingService.deregisterInstance(trpcServiceProperties.getSignature(),inetSocketAddress.getHostName(),inetSocketAddress.getPort());
            }
        } catch (NacosException e) {
            logger.error("删除服务时有错误发生:",e);
            throw new RpcException(RpcError.DEREGISTER_SERVICE_FAILED);
        }
    }

    /**
     * 注销所有注册过的服务
     */
    @Override
    public void deregisterAll(){
        if(!deregistered){
            synchronized (NacosServiceRegistryImpl.class){
                if(!deregistered){
                    Set<Map.Entry<TRPCServiceProperties,InetSocketAddress>> services = registedServices.entrySet();
                    services.forEach(service -> deregister(service.getKey(),service.getValue()));
                    deregistered=true;
                }
            }
        }
    }
}
